package com.hulk.dryad.manage.framework.operlog;

import com.google.common.base.Throwables;
import com.hulk.dryad.common.annotation.OperLog;
import com.hulk.dryad.common.constant.CommonConstants;
import com.hulk.dryad.common.util.ClassUtils;
import com.hulk.dryad.common.util.JacksonUtil;
import com.hulk.dryad.manage.framework.tenant.TenantHierarchyUtil;
import jodd.util.StringPool;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.RequestBody;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 操作日志使用spring event异步入库
 *
 * @author hulk
 */
@Slf4j
@Order
@Aspect
@RequiredArgsConstructor
public class OperLogAspect {

	private final ApplicationEventPublisher publisher;

	/**
	 * 环绕融资
	 *
	 * @param point  ProceedingJoinPoint
	 * @param operLog OperLog
	 * @return Object
	 * @throws Throwable
	 */
	@Around("@annotation(operLog)")
	public Object logAround(ProceedingJoinPoint point, OperLog operLog) throws Throwable {
		// 类和方法信息
		String strClassName = point.getTarget().getClass().getName();
		MethodSignature ms = (MethodSignature) point.getSignature();
		String strMethodName = ms.getName();
		log.info("[class]:{},[method]:{}", strClassName, strMethodName);
		OperLogEvent event = OperLogUtil.getOperLogDTO(OperLogTypeEnum.OPER);
		event.setActionType(operLog.action().getValue());
		event.setDescription(operLog.value());
		event.setClassMethod(strClassName + StringPool.HASH + strMethodName);
		event.setData(getPostJson(point, ms));
 	    event.setTenantId(TenantHierarchyUtil.getTenantId());
		// 执行时间
		long startNs = System.nanoTime();
		try {
			Object result = point.proceed();
			// 耗时
			event.setRunDuration(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs));
			event.setStatus(CommonConstants.STATUS_NORMAL);
			// 发送异步日志事件
			publisher.publishEvent(event);
			return result;
		} catch (Throwable e) {
			// 耗时
			event.setRunDuration(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs));
			// 异常详情
			event.setExceptionMsg(Throwables.getStackTraceAsString(e));
			event.setStatus(CommonConstants.STATUS_FAIL);
			// 发送异步日志事件
			publisher.publishEvent(event);
			throw e;
		}
	}

	private static String getPostJson(ProceedingJoinPoint point, MethodSignature ms) {
		Object[] args = point.getArgs();
		Method method = ms.getMethod();
		// 一次请求只能有一个 request body
		Object requestBodyValue = null;
		for (int i = 0; i < args.length; i++) {
			// 读取方法参数
			MethodParameter methodParam = ClassUtils.getMethodParameter(method, i);
			RequestBody requestBody = methodParam.getParameterAnnotation(RequestBody.class);
			// 如果是body的json则是对象
			if (requestBody != null) {
				requestBodyValue = args[i];
				break;
			}
		}
		return requestBodyValue == null ? null : JacksonUtil.toJson(requestBodyValue);
	}
}
